bitkeeper revision 1.705 (40226014Y-RJhXC4XKyIIvcyJtSHuQ)
authorkaf24@scramble.cl.cam.ac.uk <kaf24@scramble.cl.cam.ac.uk>
Thu, 5 Feb 2004 15:24:04 +0000 (15:24 +0000)
committerkaf24@scramble.cl.cam.ac.uk <kaf24@scramble.cl.cam.ac.uk>
Thu, 5 Feb 2004 15:24:04 +0000 (15:24 +0000)
memory.c, domain.c:
  Extend support for linear page tables by allowing page directories to map one another.

xen/common/domain.c
xen/common/memory.c

index 8e3338f7b5d425259c6155ceadc631aead043319..139ff3742da109fb45f793ec5444e968b24f1ac2 100644 (file)
@@ -343,6 +343,7 @@ void free_all_dom_mem(struct task_struct *p)
 {
     struct list_head *ent, zombies;
     struct pfn_info *page;
+    unsigned long x, y;
 
     INIT_LIST_HEAD(&zombies);
 
@@ -394,6 +395,24 @@ void free_all_dom_mem(struct task_struct *p)
         if ( test_and_clear_bit(_PGC_allocated, &page->count_and_flags) )
             put_page(page);
 
+        /*
+         * Forcibly invalidate L2 tables at this point to break circular
+         * 'linear page table' references. This is okay because MMU structures
+         * are not shared across domains and this domain is now dead. Thus L2
+         * tables are not in use so a non-zero count means circular reference.
+         */
+        y = page->type_and_flags;
+        do {
+            x = y;
+            if ( likely((x & (PGT_type_mask|PGT_validated)) != 
+                        (PGT_l2_page_table|PGT_validated)) )
+                break;
+            y = cmpxchg(&page->type_and_flags, x, x & ~PGT_validated);
+            if ( likely(y == x) )
+                free_page_type(page, PGT_l2_page_table);
+        }
+        while ( unlikely(y != x) );
+
         put_page(page);
     }
 }
index aeddc3ffe26ccc5e062ba5618db8547c9e3a2e01..4d3201706c84d0759de677791d97fc32369a578e 100644 (file)
@@ -346,11 +346,22 @@ static int get_page_and_type_from_pagenr(unsigned long page_nr,
 
 
 /*
- * We allow an L2 table to map itself, to achieve a linear p.t. Note that this
- * does not raise any reference counts.
+ * We allow an L2 tables to map each other (a.k.a. linear page tables). It
+ * needs some special care with reference counst and access permissions:
+ *  1. The mapping entry must be read-only, or the guest may get write access
+ *     to its own PTEs.
+ *  2. We must only bump the reference counts for an *already validated*
+ *     L2 table, or we can end up in a deadlock in get_page_type() by waiting
+ *     on a validation that is required to complete that validation.
+ *  3. We only need to increment the reference counts for the mapped page
+ *     frame if it is mapped by a different L2 table. This is sufficient and
+ *     also necessary to allow validation of an L2 table mapping itself.
  */
-static int check_linear_pagetable(l2_pgentry_t l2e, unsigned long pfn)
+static int get_linear_pagetable(l2_pgentry_t l2e, unsigned long pfn)
 {
+    unsigned long x, y;
+    struct pfn_info *page;
+
     if ( (l2_pgentry_val(l2e) & _PAGE_RW) )
     {
         MEM_LOG("Attempt to create linear p.t. with write perms");
@@ -359,8 +370,27 @@ static int check_linear_pagetable(l2_pgentry_t l2e, unsigned long pfn)
 
     if ( (l2_pgentry_val(l2e) >> PAGE_SHIFT) != pfn )
     {
-        MEM_LOG("L2 tables may not map _other_ L2 tables!\n");
-        return 0;
+        /* Make sure the mapped frame belongs to the correct domain. */
+        if ( unlikely(!get_page_from_pagenr(l2_pgentry_to_pagenr(l2e))) )
+            return 0;
+
+        /*
+         * Make sure that the mapped frame is an already-validated L2 table. 
+         * If so, atomically increment the count (checking for overflow).
+         */
+        page = &frame_table[l2_pgentry_to_pagenr(l2e)];
+        y = page->type_and_flags;
+        do {
+            x = y;
+            if ( unlikely((x & PGT_count_mask) == PGT_count_mask) ||
+                 unlikely((x & (PGT_type_mask|PGT_validated)) != 
+                          (PGT_l2_page_table|PGT_validated)) )
+            {
+                put_page(page);
+                return 0;
+            }
+        }
+        while ( (y = cmpxchg(&page->type_and_flags, x, x + 1)) != x );
     }
 
     return 1;
@@ -406,7 +436,7 @@ static int get_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn)
 
     if ( unlikely(!get_page_and_type_from_pagenr(
         l2_pgentry_to_pagenr(l2e), PGT_l1_page_table)) )
-        return check_linear_pagetable(l2e, pfn);
+        return get_linear_pagetable(l2e, pfn);
 
     return 1;
 }
@@ -434,7 +464,10 @@ static void put_page_from_l1e(l1_pgentry_t l1e)
 }
 
 
-/* NB. Virtual address 'l2e' maps to a machine address within frame 'pfn'. */
+/*
+ * NB. Virtual address 'l2e' maps to a machine address within frame 'pfn'.
+ * Note also that this automatically deals correctly with linear p.t.'s.
+ */
 static void put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn)
 {
     ASSERT(l2_pgentry_val(l2e) & _PAGE_PRESENT);